#include <math.h>
#include "Vector3D.h"

namespace Seegiri
{
    namespace Math
    {
        Vector3D::Vector3D()
        {
            m_v[0] = 0;
            m_v[1] = 0;
            m_v[2] = 0;
        }

        Vector3D::Vector3D(float _x, float _y, float _z)
        {
            m_v[0] = _x;
            m_v[1] = _y;
            m_v[2] = _z;
        }

        Vector3D::~Vector3D()
        {
        }

        Vector3D::Vector3D(const Vector3D& v)
        {
            m_v[0] = v.x();
            m_v[1] = v.y();
            m_v[2] = v.z();    
        }

        void Vector3D::zero()
        {
            m_v[0] = 0;
            m_v[1] = 0;
            m_v[2] = 0;
        }

        float Vector3D::magnitude()
        {
            return sqrt ((m_v[0]*m_v[0]) + (m_v[1]*m_v[1]) + (m_v[2]*m_v[2]));
        }

        float Vector3D::length()
        {
            return magnitude();
        }

        void Vector3D::normalize()
        {
            float mag = magnitude();

            if (mag < 0.001f)
                return;

            m_v[0] /= mag;
            m_v[1] /= mag;
            m_v[2] /= mag;
        }

        float Vector3D::dot(const Vector3D& v)
        {
            return (m_v[0]*v.x() + m_v[1]*v.y() + m_v[2]*v.z());
        }

        const Vector3D Vector3D::cross(const Vector3D& v)
        {
            Vector3D temp (
                        m_v[1] * v.z() - v.y() * m_v[2],
                        m_v[2] * v.x() - v.z() * m_v[0],
                        m_v[0] * v.y() - v.x() * m_v[1]
                    );
            return temp;
        }

        float Vector3D::x() const
        {
            return m_v[0];
        }

        float Vector3D::y() const
        {
            return m_v[1];
        }

        float Vector3D::z() const
        {
            return m_v[2];
        }

        float * Vector3D::ptr()
        {
            return &m_v[0];
        }

        Vector3D& Vector3D::operator=(const Vector3D& v)
        {
            m_v[0] = v.x();
            m_v[1] = v.y();
            m_v[2] = v.z();
            return (*this);   
        }

        bool Vector3D::operator==(const Vector3D& v)
        {
            return ((m_v[0] == v.x()) && (m_v[1] == v.y()) && (m_v[2] == v.z()));
        }

        bool Vector3D::operator!=(const Vector3D& v)
        {
            return ((m_v[0] != v.x()) || (m_v[1] != v.y()) || (m_v[2] != v.z()));
        }

        const Vector3D Vector3D::operator-()
        {
            return Vector3D(-m_v[0], -m_v[1], -m_v[2]);
        }

        const Vector3D Vector3D::operator+(const Vector3D& v)
        {
            return Vector3D (
                        m_v[0] + v.x(),
                        m_v[1] + v.y(),
                        m_v[2] + v.z()
                    );
        }

        Vector3D& Vector3D::operator+=(const Vector3D& v)
        {
            m_v[0] += v.x();
            m_v[1] += v.y();
            m_v[2] += v.z();
            return (*this);
        }

        const Vector3D Vector3D::operator-(const Vector3D& v)
        {
            return Vector3D (
                        m_v[0] - v.x(),
                        m_v[1] - v.y(),
                        m_v[2] - v.z()
                    );
        }

        Vector3D& Vector3D::operator-=(const Vector3D& v)
        {
            m_v[0] -= v.x();
            m_v[1] -= v.y();
            m_v[2] -= v.z();
            return (*this);
        }

        const Vector3D Vector3D::operator*(const Vector3D& v)
        {
            return Vector3D (
                        m_v[0] * v.x(),
                        m_v[1] * v.y(),
                        m_v[2] * v.z()
                    );
        }

        Vector3D& Vector3D::operator*=(const Vector3D& v)
        {
            m_v[0] *= v.x();
            m_v[1] *= v.y();
            m_v[2] *= v.z();
            return (*this);
        }

        const Vector3D Vector3D::operator/(const Vector3D& v)
        {
            return Vector3D (
                        m_v[0] / v.x(),
                        m_v[1] / v.y(),
                        m_v[2] / v.z()
                    );
        }

        Vector3D& Vector3D::operator/=(const Vector3D& v)
        {
            m_v[0] /= v.x();
            m_v[1] /= v.y();
            m_v[2] /= v.z();
            return (*this);
        }
        
        const Vector3D Vector3D::operator*(const float f)
        {
            return Vector3D(
                        m_v[0] * f,
                        m_v[1] * f,
                        m_v[2] * f
                    );
        }

        Vector3D& Vector3D::operator*=(const float f)
        {
            m_v[0] *= f;
            m_v[1] *= f;
            m_v[2] *= f;
            return (*this);
        }

        const Vector3D Vector3D::operator/(const float f)
        {
            return Vector3D (
                        m_v[0] / f,
                        m_v[1] / f,
                        m_v[2] / f
                    );
        }

        Vector3D& Vector3D::operator/=(const float f)
        {
            m_v[0] /= f;
            m_v[1] /= f;
            m_v[2] /= f;
            return (*this);
        }
    }
}
